%code requires{
#include "structures/node.h"
}

%{
	#include <stdio.h>
    #include <stdlib.h>
	#include <string.h>
	#include "dot_util.h"
	#include "sem_util.h"
    #include "gen_util.h"
    #include "structures/primitives.h"


	int yylex(void);
	void yyerror(const char * msg);
	int ligne;
	extern FILE * yyin;
	FILE * progo;
	int debug2 = 1;
	FILE * dot;
    symtable *TS_var,*TS_func;
    typage recupType(char* type);
    param* listeParamAppelFunc;
    node* arbre;

%}
%union
{ 
	node* nodeT;
	char* stringT;
	int intT;
	float floatT;
    param* paramT;
    stringBox* listeStringT;
}

%token 
/* mots */
TYPE IDENTIFIANT ENTIER DECIMAL BOOLEAN
/* parentheses */
PARENTHESEOUVRANTE PARENTHESEFERMANTE CROCHETOUVRANT CROCHETFERMANT
/* operations arithmetiques */
OPAFFECTATION PLUS MOINS DIV MULT UNMOINS
/* operations booleenes */
NON ET OU
/* comparaisons */
INF INFEGAL SUP SUPEGAL EGAL DIFF
/* terminaisons */
FININSTRUCTION
/*separateur*/
DEUXPOINT VIRGULE
/* mots reserves */
IF THEN ELSE WHILE IS FUNCTION PROGRAM NEW FREE RETURN 
_BEGIN END DO PROCEDURE

/* le type node */
%type <nodeT> 
PROGRAM TYPE  
FUNCTION PROCEDURE
OPAFFECTATION PLUS DIV MULT DEUXPOINT
INF INFEGAL SUP SUPEGAL EGAL DIFF
FININSTRUCTION
IF THEN ELSE WHILE 
_BEGIN END NEW RETURN
RHS LHS NON expressionArithmetique expressionBooleenne
PARENTHESEOUVRANTE PARENTHESEFERMANTE CROCHETOUVRANT CROCHETFERMANT 
UNMOINS MOINS
program ListeInstruction Instruction expression ListeProcedure Main Procedure
AppelFonction ListeComponent Component ListeParam


%type <stringT>
IDENTIFIANT ENTIER DECIMAL BOOLEAN FREE StatementConditionnel Statement

%type <paramT>
D_ListeParam D_Param Param

%type <listeStringT>
ListeIdentifiant Declaration ListeDeclaration

/* Définition des priorités */
%left PLUS MOINS
%left MULT DIV
%left UNMOINS
%left NON
%left ET OU
//%right RETURN

%start program

%%
program	: ListeProcedure Main
			{
                //printf("%s\n","program -> listeP main");
                //arbre
                node *n1 = (node *) $1;
                node *n2 = (node *) $2;
                node *last = (node *) n1;
                while(last->frere)
                {
                        last = last->frere;
                }
                link_brothers(last,n2);
                node *tmp = n1;
                //printf("__________________bvbvobvsob____________\n");
                while(tmp)
                {
                    //printf("%s | ",tmp->lexeme);
                    tmp = tmp -> frere;
                }
                //printf("\n");
                char *s = strdup("program");
                node *n=mknode(s,NULL,TPROGRAM,NULL,n1,NULL);
                $$=n;
                arbre = n;
                //dot
                createnode(dot,n,n->lexeme);
                node *brother = n1;
                while(brother)
                {
                    linknode(dot,n,brother);
                    brother = brother->frere;
                }
			}
			| Main
			{
                //printf("%s\n","program -> main");
                //arbre
                node *n1 = (node *) $1;
                char *s = strdup("program");
                node *n=mknode(s,NULL,TPROGRAM,NULL,n1,NULL);
                $$=n;
                //dot
                createnode(dot,n,n->lexeme);
                node *brother = n1;
                while(brother)
                {
                    linknode(dot,n,brother);
                    brother = brother->frere;
                }
			}
			;

Main :		PROGRAM Statement
			{
                //arbre
                //printf("%s\n","main -> program statement ");
                char *s = strdup("Main");
                node *n=mknode(s,NULL,TMAIN,NULL,$2,NULL);
                $$=n;
                //dot
                createnode(dot,n,n->lexeme);
                linknode(dot,n,$2);
			}
			;

ListeProcedure : 
            Procedure
            {
                //printf("%s\n","procedure -> procedure ");
                $$=$1;
            }
            | Procedure ListeProcedure
            {
                //printf("%s\n","procedure -> listeP");
                node *n1= (node *) $1;
                node *n2= (node *) $2;
                link_brothers(n1,n2);
                //printf("$1 : %p -> frere : %p\n",n1);
                //printf("$2 : %p -> frere : %p\n",n2);
                $$=$1;
            }
            ;

Procedure : 
            FUNCTION IDENTIFIANT PARENTHESEOUVRANTE D_ListeParam PARENTHESEFERMANTE DEUXPOINT TYPE IS Statement
            {
                    //arbre
                    char *s;
                    asprintf(&s,"%s%s","",$2);
                    //printf("%s\n","procedure -> function %s",s);
                    node *c = (node*) $9;
                    
                    // on récupere le léxème de l'identifiant de la fonction
                    char* funcId = (char *) $2;
                    
                    // on récupére le léxème du type
                    char* type = (char *) $7;
                    
                    // on regarde le type associé à ce léxème                                          
                    typage t = recupType(type);
					
					if ($4 != NULL)
						//ajouter dans la table de symboles des fonctions
                        addDeclaration(0,funcId,t,$4,TS_func);
						// desallouer ????????
					else
                        addDeclaration(0,funcId,t,NULL,TS_func);
                    
                    entree* e = getEntry(funcId,TS_func);
                    node *n = mknode(s,e,TFUNCTION,NULL,c,NULL);

					// on réinitialise le type de retour pour la suite
					typeRetourFunc = ERR;

                    //ajout dans l'arbre
                    $$=n;

                    //dot
                    createnode(dot,n,n->lexeme);
                    linknode(dot,n,$9);
            }
            | PROCEDURE IDENTIFIANT PARENTHESEOUVRANTE D_ListeParam PARENTHESEFERMANTE IS Statement
            {
					//printf("%s\n","procedure -> procedure %s",$2);
                    //arbre
                    char *s;
                    asprintf(&s,"%s%s","",$2);

					// on récupere le léxème de l'identifiant de la procedure
                    char* procId = $2;

                    typage t = VOID;

                    //ajouter dans la table de symboles des fonctions
                    addDeclaration(0,procId,t,$4,TS_func);
                    
                    // onrécupere l'entrée que l'on vient de créer
                    entree* e = getEntry(procId,TS_func);
                    // on créer le noeud
                    node *n=mknode(s,e,TPROCEDURE,NULL,$7,NULL);
                    
                    $$=n;
                    //dot
                    createnode(dot,n,n->lexeme);
                    linknode(dot,n,$7);
            }
            ;
//liste des parametres pour la declaration d'une fonction
D_ListeParam :
			{
				$$ = NULL;
			}
			| Param
			;
Param :
			D_Param
            {
                $$ = $1;
            }
            | D_Param VIRGULE Param
            {
                $1->next = $3;
                $$ = $1;
            }
            ;

D_Param : 
            IDENTIFIANT DEUXPOINT TYPE
            {
                // on récupère le type
                char* type =(char *) $3;
                typage t = recupType(type);
                // création de la liste de params
                param* p = (param*)malloc(sizeof(param));
                p->type = t;
                p->lexeme = (char*)  $1;
                $$ = p;
            }
            ;

expressionBooleenne	:
			BOOLEAN
			{
            	char* s = yyval.stringT;
                entree* e = createFakeEntry(BOOL);
                $$ = mkleafid(s,e,TCST);
				createnode(dot,$$,s);	
			}
			|
			expressionArithmetique INF expressionArithmetique
			{
				$$ = opbinaire("<",$1,$3,dot);
                //printf("%s => retour %p \n", "expA -> expA < expA", $$);
                setType((entree *)$$->info,BOOL);
			}
			|	expressionArithmetique INFEGAL expressionArithmetique
			{
				$$ = opbinaire("<=",$1,$3,dot);
                //printf("%s => retour %p \n", "expA -> expA <= expA", $$);
                setType((entree *)$$->info,BOOL);
			}
			|	expressionArithmetique SUP expressionArithmetique
			{
				$$ = opbinaire(">",$1,$3,dot);
                //printf("%s => retour %p \n", "expA -> expA > expA", $$);
                setType((entree *)$$->info,BOOL);
			}
			|	expressionArithmetique SUPEGAL expressionArithmetique
			{
				$$ = opbinaire(">=",$1,$3,dot);
                //printf("%s => retour %p \n", "expA -> expA >= expA", $$);
                setType((entree *)$$->info,BOOL);
			}
			|	expressionArithmetique EGAL expressionArithmetique
			{
				$$ = opbinaire("=",$1,$3,dot);
                //printf("%s => retour %p \n", "expA -> expA = expA", $$);
                setType((entree *)$$->info,BOOL);
			}
			|	expressionArithmetique DIFF expressionArithmetique
			{
				$$ = opbinaire("<>",$1,$3,dot);
                //printf("%s => retour %p \n", "expA -> expA <> expA", $$);
                setType((entree *)$$->info,BOOL);
			}
			|	NON expressionBooleenne
			{			
                $$ = mknode("!",NULL,TNON,NULL,$2,NULL);
                entree* e = createFakeEntry(getType((entree *)$2->info));
                $$->info = (info *) e;
                createnode(dot,$$,$$->lexeme);
                linknode(dot,(node *)$$,(node *)$2);
                setType((entree *)$$->info,BOOL);
           
			}
			|	expressionBooleenne ET expressionBooleenne
			{
				$$ = opbinaire("&&",$1,$3,dot);
                //printf("%s => retour %p \n", "expA -> expA && expA", $$);
                setType((entree *)$$->info,BOOL);
			}
			|	expressionBooleenne OU expressionBooleenne
			{
				$$ = opbinaire("||",$1,$3,dot);
                //printf("%s => retour %p \n", "expA -> expA || expA", $$);
                setType((entree *)$$->info,BOOL);
			}
			|	PARENTHESEOUVRANTE expressionBooleenne PARENTHESEFERMANTE
			{
                $$=$2;
			}
			;

ListeComponent :
			Component
			{
				$$=$1;
			}
			|Component ListeComponent
			{
				node* n1 = $1;
				node* n2 = $2;
				//arbre
                node *c = n1;
                node *prec = n1;
                while(c)
                {
                    prec = c;
                    c = c->frere;
                }
                link_brothers(prec,n2);
                $$=n1;   												
			}
			;

Component :
			StatementConditionnel
			{				
                $$=(node *)$1;
			}
			|ListeInstruction
			{
                $$=(node *)$1;
			}
			;

Statement : 
            Instruction
            {
                $$=(node *)$1;
            }
            | IF expressionBooleenne THEN Statement ELSE Statement
            {
                node *n1 = (node *) $2;
                node *n2 = (node *) $4;
                node *n3 = (node *) $6;
                link_brothers(n2,n3);
                link_brothers(n1,n2);                                     
                node* n;                
				n = mknode("if", NULL,TIF,NULL,n1, NULL);
				createnode(dot,n,n->lexeme);				
				node *brother = n1;                
                while(brother)
                {
                    linknode(dot,n,brother);
                    brother = brother -> frere;
                }                
                $$=(node *)n;
            }
            | WHILE expressionBooleenne DO Statement
            {
                node *n1 = (node *) $1;
                node *n2 = (node *) $2;
                node *n3 = (node *) $4;
                //arbre
                link_brothers(n2,n3);
                node* n = mknode("while", NULL,TWHILE,NULL,n2, NULL);
                $$=n;
                //dot
                createnode(dot,n,n->lexeme);
                node *brother = n2;                
                while(brother)
                {
                    linknode(dot,n,brother);
                    brother = brother -> frere;
                }                                                
            }
			| _BEGIN ListeDeclaration ListeComponent END
			{
				//arbre
				node* n = mknode("bloc",NULL,TBLOC,(void*)$2,$3,NULL);
                $$=(node *)n;
				//dot
				createnode(dot,n,n->lexeme);

                node *brother = (node *) $3;
                while(brother)
                {
                    linknode(dot,n,brother);
                    brother = brother -> frere;
                }     
				
			}
			| _BEGIN ListeComponent END
			{				
				//arbre
				node* n = mknode("bloc",NULL,TBLOC,NULL,$2,NULL);
                $$=(node *)n;
				//dot
				createnode(dot,n,n->lexeme);

				node *brother = $2;               
                while(brother)
                {
                    linknode(dot,n,brother);
                    brother = brother -> frere;
                }   
				
			}
            ;

StatementConditionnel : 
			IF expressionBooleenne THEN Statement ELSE Statement
			{
				node *n1 = $2;
                node *n2 = $4;
                node *n3 = $6;                                
                link_brothers(n2,n3);
                link_brothers(n1,n2);                      
                node* n;                
				n = mknode("if", NULL,TIF,NULL,n1, NULL);
				createnode(dot,n,n->lexeme);				
				node *brother = n1;                
                while(brother)
                {
                    linknode(dot,n,brother);
                    brother = brother -> frere;
                }                
                $$=n;
			}
			| WHILE expressionBooleenne DO Statement
			{
				node *n1 = $1;
                node *n2 = $2;
                node *n3 = $4;
                //arbre
                link_brothers(n2,n3);
                node* n = mknode("while", NULL, TWHILE, NULL, n2, NULL);
                $$=n;
                //dot
                createnode(dot,n,n->lexeme);
                node *brother = n2;                
                while(brother)
                {
                    linknode(dot,n,brother);
                    brother = brother -> frere;
                }                    
			}
			| _BEGIN ListeDeclaration ListeComponent END
			{
				//arbre
				node* n = mknode("bloc",NULL,TBLOC,$2,$3,NULL);
				$$=n;
				//dot
				createnode(dot,n,n->lexeme);

				node *brother = $3;               
                while(brother)
                {
                    linknode(dot,n,brother);
                    brother = brother -> frere;
                }    
			}
			| _BEGIN ListeComponent END
			{
				//arbre
				node* n = mknode("bloc",NULL,TBLOC,NULL,$2,NULL);
				$$=n;
				//dot
				createnode(dot,n,n->lexeme);

				node *brother = $2;               
                while(brother)
                {
                    linknode(dot,n,brother);
                    brother = brother -> frere;
                }  
			}
			;

ListeInstruction : 
            Instruction FININSTRUCTION ListeInstruction
            {
                node *n1 = $1;
                node *n2 = $3;
                link_brothers(n1,n2);
                $$ = n1;
            }
            | Instruction
            {
                $$=$1;
            }
            ;

ListeDeclaration : 
            Declaration FININSTRUCTION
            {	
				$$ = $1;
            }
            | Declaration FININSTRUCTION ListeDeclaration 
            {
				stringBox* tmp = $1;
				
				while(tmp-> next!=NULL)
				{
					tmp = tmp->next;
				}
				
				tmp->next = $3;
				
				$$ = $1;
            }
            ;

ListeIdentifiant : 
            IDENTIFIANT
            {
            	//table des symboles
                stringBox* liste = (stringBox*)malloc(sizeof(stringBox));                
                char* s =$1;

                liste->stringId = s;
                liste->next = NULL;

                $$ = liste;
            }
            | IDENTIFIANT VIRGULE ListeIdentifiant
            {
				stringBox* liste = (stringBox*)malloc(sizeof(stringBox));
                char* s = $1;

                liste->stringId = s;
                liste->next = $3;

                $$ = liste;
            }
            ;

Declaration : 
            TYPE ListeIdentifiant
            {
                char* type = $1;

                typage t = recupType(type);
                
                stringBox* tmp = $2;
                while(tmp->next != NULL)
                {
					//printf("-------------------------- %s !!! \n",tmp->stringId);
                    tmp->type = t;
                    tmp = tmp->next;
                }
                //printf("-------------------------- %s !!! \n",tmp->stringId);
                tmp->type = t;
                
                $$ = $2;
            }
            ;

LHS : 
            IDENTIFIANT
            {            				
            	char* s = yyval.stringT;
                //entree* e = getEntry(s,TS_var);
            	$$ = mkleafid(s,NULL,TVAR);
				createnode(dot,$$,s);

            }
            | IDENTIFIANT CROCHETOUVRANT expressionArithmetique CROCHETFERMANT
            {
                $$ = array(dot,$1,$3);
            }
            ;

expressionArithmetique : 
			expressionArithmetique PLUS expressionArithmetique  		
			{ 				
				$$ = opbinaire("+",$1,$3,dot);
                //printf("%s => retour %p \n", "expA -> expA + expA", $$);
			}
			| expressionArithmetique MOINS expressionArithmetique
			{
				$$ = opbinaire("-",$1,$3,dot);
                //printf("%s => retour %p \n", "expA -> expA - expA", $$);
			}
			| expressionArithmetique DIV expressionArithmetique 
			{
				//printf("%s\n", "expA -> expA / expA");
				$$=opbinaire("/",$1,$3,dot);
			}
			| expressionArithmetique MULT expressionArithmetique
			{
				//printf("%s\n", "expA -> expA * expA");
				$$=opbinaire("*",$1,$3,dot);
			}
			| MOINS expressionArithmetique %prec UNMOINS
			{
				$$ = mknode("-",NULL,TMOINSUNAIRE,NULL,$2,NULL);
                entree* e = createFakeEntry(getType((entree *)$2->info));
                $$->info = e;
                createnode(dot,$$,$$->lexeme);
                linknode(dot,$$,$2);
			}
			| ENTIER 													
			{
				char *s = yyval.stringT;
                int i= atoi(s);
                //printf(" node creer cst_int : %p\n", $$);
                //printf("before add \n");
                entree * e = addDeclaration(-1,s,INT,NULL,TS_var);
                //printf("after add \n");
                $$ = mkleafcst(s, e,TCST);
                createnode(dot,$$,$$->lexeme);
			}
			| DECIMAL
			{
				//printf("%s\n", "expA -> constante DECIMAL");
                char *s = yyval.stringT;
                float f = atof(s);
                entree* e = addDeclaration(-1,s,FLOAT,NULL,TS_var);
                $$ = mkleafcst(s, e,TCST);
                createnode(dot,$$,$$->lexeme);


			}
			| IDENTIFIANT CROCHETOUVRANT expressionArithmetique CROCHETFERMANT
			{
                $$=array(dot,$1,$3);
			}
			| PARENTHESEOUVRANTE expressionArithmetique PARENTHESEFERMANTE
			{
                $$=$2;
			}
			| IDENTIFIANT 												
			{ 
                char* s = yyval.stringT;
                //entree* e = getEntry(s,TS_var);
                $$ = mkleafid(s,NULL,TVAR);
                createnode(dot,$$,s);
			}
			;

Instruction :     
			LHS OPAFFECTATION RHS
			{
                //printf("aff => $3 : %p \n", $3);
				$$=opbinaire(":=",$1,$3,dot);
			}
			| FREE PARENTHESEOUVRANTE expressionArithmetique PARENTHESEFERMANTE
			{
                
			}
            | AppelFonction 
            {
                $$=$1;
            }
			| RETURN expressionArithmetique
			{
				node *n=mknode("return",NULL,TRETURN,NULL,$2,NULL);
				$$=n;
				//dot
				createnode(dot,n,n->lexeme);
				node *brother = $2;
				while(brother)
				{
					linknode(dot,n,brother);
					brother=brother->frere;
				}
			}
			| RETURN
			{
				node *n=mknode("return",NULL,TRETURN,NULL,NULL,NULL);
				$$=n;
				//dot
				createnode(dot,n,n->lexeme);
			}
            ;

AppelFonction :   
            IDENTIFIANT PARENTHESEOUVRANTE ListeParam PARENTHESEFERMANTE
            {
                char *s;
                asprintf(&s,"%s%s","",yyval.stringT);
                node *n= mknode(s,NULL,TCALLFUN,NULL,$3,NULL);
                node *n2 = $3;
                createnode(dot,n,n->lexeme);
                node *brother = n2;
                
                while(brother)
                {
                    linknode(dot,n,brother);
                    brother = brother -> frere;
                }                   
                
                $$=n;
            };
ListeParam : 
            expressionArithmetique
            {
                $$=$1;
            }
            |expressionArithmetique VIRGULE ListeParam
            {
                node *n1 = $1;
                node *n2 = $3;
                link_brothers(n1,n2);
            }
            ;

RHS :   
            NEW IDENTIFIANT CROCHETOUVRANT expressionArithmetique CROCHETFERMANT
            {
                $$=array(dot,$2,$4);
            }
            | AppelFonction
            {
                $$=$1;
            }
			| expression
			{
				node *n = $1;
                //printf("%s => %p \n", "RHS -> exp",n);
				$$=n;
			}
            ;

//a garder ?
expression :	expressionArithmetique
				{
					$$ = $1;
                    //printf("%s => %p \n", "exp -> expA",$$);
				}
				| expressionBooleenne
				{
					$$=$1;
                    //printf("%s => %p \n", "exp -> expB",$$);

				}
                ;					

%%
void yyerror(const char * msg) {
	printf( "%s.\n",msg);
}

void alloc_ts(symtable **var)
{
    *var = creerSymtable();
}

typage recupType(char* type)
{
    typage res;
    if (strcmp(type,"integer")==0)
    {
        res = INT;
    }
    else if ((strcmp(type,"float")==0))
    {
        res = FLOAT;
    }
    else
    {
        res = ERR;
    }
    //affichage de debug
    /*switch(res)
    {
        case INT : printf("***********type INT\n"); break;
        case BOOL : printf("***********type BOOL\n"); break;
        case FLOAT : printf("***********type FLOAT\n"); break;
        case ADDRESS : printf("***********type ADDRESS\n"); break;
        case ERR : printf("***********type ERR\n"); break;
        case VOID : printf("***********type VOID (=procedure)\n"); break;
    }*/

    return res;
}



int main(int argc, char ** argv) {
	ligne = 1;
	if (argc>1) yyin = fopen(argv[1],"r");
	progo = fopen("prog.o","w");
	dot = fopen("dot.out","w");
	fprintf(dot, "digraph G {\n");

    FILE *verif = fopen("v.out","w");
    fprintf(verif, "digraph G {\n");
    //creation des table de symboles
    alloc_ts(&TS_var);
    alloc_ts(&TS_func);
    //allocation de la liste des paramtres de l'appel d'une function
    listeParamAppelFunc = (param*)malloc(sizeof(param));
    //analyse
	if (!yyparse()) printf("[Analyse sémantique amorcée sans encombres]\n\n");

    dot_DFS(arbre,verif);
    fprintf(verif," \n};\n");
	fclose(progo); 
	fprintf(dot," \n};\n");


	fclose(dot);
	// on enregistre les fonctions prédéfinie : print
	param* paramsPrint = (param*)malloc(sizeof(param));
	paramsPrint->lexeme = "x";
	paramsPrint->type = INT;
	addDeclaration(0,"print",VOID,paramsPrint,TS_func);
	// verif typage :
	if(tree_DFS(arbre,TS_func,TS_var)==NODE_OK)
	{
    // génération de code intermédiaire
		FILE * codeInt = fopen("code.int","w");
		gen_DFS(arbre,codeInt, TS_func, TS_var);
		printf("[Compilation terminée]\n");
	}
    else
    {
        printf("[Erreur lors de l'analyse du typage]\n");
    }
	return 0;
}

